package access

import (
	"errors"
	"fmt"
	"gitlab.local/DO-module/new-filemanage-module/src/core/sqlitedb"
	"gitlab.local/DO-module/new-filemanage-module/src/core/sqlitedb/sqlitemodel"
	"gitlab.local/DO-module/new-filemanage-module/src/service/access/constant"
	"gitlab.local/golibrary/apputils"
	"gitlab.local/golibrary/user"
	"gitlab.local/golibrary/utils"
	"gorm.io/gorm"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// AccessMap[userName/groupName]=isAllow
type AccessMap map[string]int

var AppEnable = []string{"FTP", "SFTP", "SMB"}

/**
CREATE TABLE "app_access_users" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"app_id" varchar(255) NOT NULL DEFAULT '',"username" VARCHAR(255) NOT NULL DEFAULT '',"is_allow" INTEGER NOT NULL DEFAULT 0);
*/
// 获取应用权限列表
func (ac *Access) GetAppsList() (result []AppsList, err error) {
	result = make([]AppsList, 0)
	userlists := ac.getAllusers()
	alldata := GetAllAppAccessList()
	accessmap := ac.AccessMapAppid()
	groupmap := ac.getAdminGroup()
	if len(alldata) > 0 {
		for _, item := range alldata {
			res := AppsList{}
			//conffile := filepath.Join(constant.AccessConf, item.AppId)
			res.AppId = item.AppId
			res.AppName = item.AppName
			//Disallow := constant.ReadFileLine(conffile)
			res.AllowUserList, res.DisallowUserList = ac.GetCountAccess(userlists, accessmap, item, groupmap)
			result = append(result, res)
		}
	}
	return
}
func GetAdmin() string {
	return os.Getenv("admin")
}

// CheckAppAccessByAccessMap
func CheckAppAccessByAccessMap(data AccessMap) bool {
	code := false
	for name, val := range data {
		if strings.HasPrefix(name, "@") {
			code = true
			if val == 0 {
				code = false
			}
		} else {
			code = true
			if val == 0 {
				code = false
			}
			break
		}
	}
	return code
}

// CheckAppDefault 查看此应用是不是几个默认的应用之一
func CheckAppDefault(appid string) bool {
	for _, item := range DefaultServe {
		if item.AppId == appid {
			return true
		}
	}
	return false
}

func (ac *Access) AccessMapAppid() map[string]AccessMap {
	allAccess, err := ac.getAllAppAccess()
	result := make(map[string]AccessMap, 0)
	if err != nil || len(allAccess) == 0 {
		return result
	}

	for _, item := range allAccess {
		res := make(AccessMap, 0)
		res[item.Username] = item.IsAllow
		_, ok := result[item.AppId]
		if ok {
			result[item.AppId][item.Username] = item.IsAllow
		} else {
			result[item.AppId] = res
		}
	}

	return result
}

func GetAllAppAccessList() (alldata []AccessDatas) {
	installlist := apputils.GetLocalApps()
	alldata = make([]AccessDatas, 0)
	if len(installlist) > 0 {
		for _, item := range installlist {
			res := AccessDatas{}
			res.AppId = item.ID
			res.AppName = item.Name
			alldata = append(alldata, res)
		}
	}
	alldata = append(DefaultServe, alldata...)
	return
}

func (ac *Access) getAllusers() (res []string) {
	ADMIN := GetAdmin()
	userlists, err := ac.getallUsers()
	if err == nil && len(userlists) > 0 {
		for _, item := range userlists {
			if strings.HasPrefix(item.Username, "@") {
				continue
			}
			if item.Username == "admin" && !IsFirstAdmin(ADMIN, item.Username) {
				continue
			}
			res = append(res, item.Username)
		}
	}
	return
}

func (ac *Access) getUserAccessMap(data AccessMap, name string) AccessMap {
	result := make(AccessMap, 0)
	gs, err := ac.getRowsFromGroupUsers(name)
	if err == nil && len(gs) > 0 {
		for _, item := range gs {
			item1, ok := data[item.GroupName]
			if ok {
				result[item.GroupName] = item1
			}
		}
	}
	a, ok1 := data[name]
	if ok1 {
		result[name] = a
	}
	return result
}
func isContain(items []string, item string) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

func (ac *Access) isAppEnable(groupmap map[string]string, name, ADMIN string, accessMap AccessMap, ok bool, appid AccessDatas) bool {

	if ADMIN == name {
		return true
	}
	_, isNull := accessMap["guest"]
	if !isNull {
		if name == "guest" && !isContain(AppEnable, appid.AppId) {
			return false
		}
	}

	if ok {
		accessMap = ac.getUserAccessMap(accessMap, name)
		if len(accessMap) > 0 {
			if CheckAppAccessByAccessMap(accessMap) {
				return true
			}
		} else {
			if appid.IsDefault {
				return true
			}
		}
	} else {
		_, isadmin := groupmap[name]
		if isadmin {
			return true
		} else {
			if appid.IsDefault {
				return true
			}
		}
	}

	return false
}

func (ac *Access) GetCountAccess(userlist []string, accessmap map[string]AccessMap, appid AccessDatas, groupmap map[string]string) ([]string, []string) {
	a := make([]string, 0)
	b := make([]string, 0)

	ADMIN := GetAdmin()
	item11, ok := accessmap[appid.AppId]
	if len(userlist) <= 0 {
		return a, b
	}
	for _, name := range userlist {
		if ac.isAppEnable(groupmap, name, ADMIN, item11, ok, appid) {
			a = append(a, name)
		} else {
			b = append(b, name)
		}
	}
	return a, b
}

// 查询 app_access_user 表中的全部数据
func (ac *Access) getAllAppAccess() (appAccessUser []sqlitemodel.AppAccessUser, err error) {
	db := ac.db.DbGet()
	err = db.Model(sqlitemodel.AppAccessUser{}).Find(&appAccessUser).Error
	return
}

func (ac *Access) getAppidAccessMap(appid string) AccessMap {
	db := ac.db.DbGet()
	result := make(AccessMap, 0)
	appAccessUser := make([]sqlitemodel.AppAccessUser, 0)
	err := db.Model(sqlitemodel.AppAccessUser{}).Where("app_id=?", appid).Find(&appAccessUser).Error
	if err == nil && len(appAccessUser) > 0 {
		for _, item := range appAccessUser {
			result[item.Username] = item.IsAllow
		}
	}
	return result
}

// 查询全部用户、用户组
func (ac *Access) getallUsers() (userlists []*sqlitemodel.UserTable, err error) {
	db := ac.db.DbGet()
	err = db.Model(sqlitemodel.UserTable{}).Where("password not in('adnet','adgroup')").Find(&userlists).Error
	return
}

// 获取 用户组和用户的关系
func (ac *Access) getUserGroups() (usersMapGroup []sqlitemodel.GroupUsers, err error) {
	db := ac.db.DbGet()
	err = db.Table("group_users").Find(&usersMapGroup).Error
	return
}

// getRowsFromGroupUsers 获取group_users 表中所有指定用户名的行
func (ac *Access) getRowsFromGroupUsers(username string) (usersMapGroup []sqlitemodel.GroupUsers, err error) {
	db := ac.db.DbGet()
	err = db.Table("group_users").Where("username=?", username).Find(&usersMapGroup).Error
	return
}

func (ac *Access) getAdminGroup() map[string]string {
	usersMapGroup := make([]sqlitemodel.GroupUsers, 0)
	result := make(map[string]string, 0)
	db := ac.db.DbGet()
	err := db.Table("group_users").Where("groupname=?", "@admin").Find(&usersMapGroup).Error
	if err == nil && len(usersMapGroup) > 0 {
		for _, item := range usersMapGroup {
			result[item.UserName] = item.UserName
		}
	}
	return result
}

func IsFirstAdmin(ADMIN string, user string) bool {
	return ADMIN == user
}

// GetAllAccessInfo 根据 appid 获取权限
func (ac *Access) GetAllAccessInfo(appid string) (result AppInfoAccess) {
	//conffile := filepath.Join(constant.AccessConf, appid)
	//Disallow := constant.ReadFileLine(conffile)
	userlists, err := ac.getallUsers()
	accesslists := ac.getAppidAccessMap(appid)
	isDefault := CheckAppDefault(appid)
	var users []UserAccess
	var groups []UserAccess
	ADMIN := GetAdmin()
	if err == nil && len(userlists) > 0 {
		for _, item := range userlists {
			//if item.Username == "" || item.Username == "@guest" {
			//	continue
			//}
			if item.Username == "" {
				continue
			}
			// 如果admin不是超级管理员，则过滤
			if item.Username == "admin" && !IsFirstAdmin(ADMIN, item.Username) {
				continue
			}
			// 过滤系统用户
			if item.Password == "system" {
				continue
			}

			if strings.HasPrefix(item.Username, "@") {
				username := strings.TrimPrefix(item.Username, "@")
				_, err = user.LookupGroup(username)
				if err != nil {
					continue
				}
				groups = append(groups, accessGroups(appid, item.Username, accesslists))
			} else {
				users = append(users, ac.getOneUserAccessInfo(appid, item.Username, ADMIN, accesslists, isDefault))
			}
		}
	}
	result.User = users
	result.Group = groups
	sort.Sort(UserSlice(result.User))
	sort.Sort(UserSlice(result.Group))
	return
}

func accessGroups(_ string, name string, accessMap AccessMap) (gs UserAccess) {
	gs.Username = strings.Trim(name, "@")
	_, isExistadmin := accessMap["@admin"]
	//_, isExistallusers := accessMap["@allusers"]
	if !isExistadmin {
		if name == "@admin" {
			gs.IsCheck = true
			gs.IsAllow = 1 // 默认允许
			gs.IsAdmin = true
			return
		}
	}

	//if name == "@guest" && !isContain(AppEnable, appid) {
	//	gs.Default = true
	//	gs.IsAllow = 0
	//	return
	//}
	item, ok := accessMap[name]
	if ok {
		gs.IsCheck = true
		gs.IsAllow = item
	}
	return
}

func (ac *Access) getOneUserAccessInfo(appid string, name string, admin string, accessMap AccessMap, isDefault bool) (userAccess UserAccess) {
	userAccess.Username = name
	if name == admin {
		userAccess.IsCheck = true
		userAccess.IsAllow = 1
		userAccess.IsAdmin = true
		return
	}
	if name == "guest" {
		_, isAllow := accessMap["guest"]
		if !isAllow {
			if name == "guest" {
				userAccess.IsCheck = true
			}
			if !isContain(AppEnable, appid) {
				userAccess.IsAllow = 0
				return
			}
		}
	}
	isAllow, ok := accessMap[name]
	userAccess.IsCheck = true
	if ok {
		userAccess.IsAllow = isAllow
	} else {
		// 默认用户组的权限
		flag, isadmin := ac.getUserAccessFlag(name, accessMap)
		if flag == 2 {
			// 无用户组权限，默认用户权限
			if isadmin {
				// admin 用户组里面的应用权限默认是开的
				userAccess.IsAllow = 1
			}
			// 如果是默认应用，则是开启的
			if isDefault {
				userAccess.IsAllow = 1
			}
		} else {
			//默认用户组的权限
			userAccess.IsAllow = flag
		}
	}
	return
}

// getUserAccessFlag
// return flag 2: no access right, 1: allow, 0: not allow. (allow represents possessing access right)
// return admin: true=is admin group
func (ac *Access) getUserAccessFlag(name string, accessMap AccessMap) (flag int, admin bool) {
	flag = 2
	groupUsers, err := ac.getRowsFromGroupUsers(name)
	if err == nil && len(groupUsers) > 0 {
		for _, item := range groupUsers {
			if item.GroupName == "@admin" {
				admin = true
			}
			allow, ok := accessMap[item.GroupName]
			if ok {
				flag = 1
				if allow == 0 {
					flag = 0
					break
				}
			}
		}
	}
	return
}

func (ac *Access) SetAccess(data EditAccessData) (code bool, err error) {
	db, err := sqlitedb.New()
	if err != nil {
		return
	}
	data.List.Group = ac.getGroupRelAccess(data.List.Group, data.AppId)
	data.List.User = ac.getUserRelAccess(data.List.User, data.AppId, data.List.Group)
	if len(data.List.User) <= 0 {
		return
	}
	admin := GetAdmin()
	ndb := db.DbGet().Table("app_access_user")
	err = ndb.Transaction(
		func(tx *gorm.DB) error {
			err0 := tx.Where("app_id=?", data.AppId).Delete(sqlitemodel.AppAccessUser{}).Error
			sql := ac.createAccess(data.List.User, data.AppId, true, "", admin)
			sql = ac.createAccess(data.List.Group, data.AppId, false, sql, admin)
			if sql == "" {
				return nil
			}
			sql = fmt.Sprintf("insert into app_access_user (app_id,username,is_allow) values %s", sql)
			err1 := tx.Exec(sql).Error
			if err0 != nil || err1 != nil {
				return errors.New("fail")
			}
			return nil
		})
	_ = db.CloseDb()
	//因为内部回环调用不需要过多的验证,直接给rwx权限
	path := fmt.Sprintf("/var/api/%s.sock", data.AppId)
	if utils.Exists(path) {
		_, err = utils.ShellExec(fmt.Sprintf("chmod 777 %s", path))
	}
	code = true
	setDefaultAccess(data.List.User, data.AppId)
	//setAppAcl(data.List, data.AppId)
	return
}

// 因为单独使用acl权限进行管理的话每次请求都会进行acl的修改, 太过频繁
/*func setAppAcl(info AppInfoAccess, appId string) {
	if len(info.User) == 0 || len(info.Group) == 0 {
		return
	}
	appId = "/var/api/" + appId + ".sock"
	clean := fmt.Sprintf(`setfacl -b %s`, appId)
	if _, err := utils.ShellExec(clean); err != nil {
		return
	}
	var acls []string
	for _, v := range info.User {
		if v.IsAllow != 1 || v.IsAdmin {
			continue
		}
		acls = append(acls, fmt.Sprintf(`-m u:%s:rwx '%s'`, v.Username, appId))
	}
	for _, g := range info.Group {
		if g.IsAllow != 1 || g.Username == "admin" {
			continue
		}
		acls = append(acls, fmt.Sprintf(`-m g:%s:rwx '%s'`, g.Username, appId))
	}
	if len(acls) > 0 {
		aclStr := fmt.Sprintf(`setfacl  %s`, strings.Join(acls, " "))
		if _, err := utils.ShellExec(aclStr); err != nil {
			return
		}
	}
}*/

func (ac *Access) getGroupRelAccess(data []UserAccess, appid string) []UserAccess {
	result := make([]UserAccess, 0)
	if len(data) > 0 {
		for _, item := range data {
			if !item.IsCheck {
				item.IsAllow = 1
				if !checkDefaultApp(appid) && item.Username != "admin" {
					item.IsAllow = 0
				}
			}
			result = append(result, item)
		}
	}
	return result
}

func (ac *Access) getUserRelAccess(data []UserAccess, appid string, group []UserAccess) []UserAccess {
	result := make([]UserAccess, 0)
	groupusers, err := ac.getUserGroups()
	if err != nil {
		return result
	}
	if len(data) <= 0 {
		return result
	}
	for _, item := range data {
		if item.IsCheck {
			result = append(result, item)
			continue
		}
		code, gs := getUserRelAccess(groupusers, item.Username)
		if code {
			item.IsAllow = 1
			result = append(result, item)
			continue
		}
		t := getUserGroupAccess(group, gs)
		if t != 2 {
			item.IsAllow = t
		} else {
			if checkDefaultApp(appid) {
				item.IsAllow = 1
			} else {
				item.IsAllow = 0
			}
		}
		result = append(result, item)
	}
	return result
}

func getUserGroupAccess(group []UserAccess, gs []string) int {
	t := 2
	if len(group) > 0 && len(gs) > 0 {
		for _, g := range group {
			if !g.IsCheck {
				continue
			}
			for _, g1 := range gs {
				if g1 == g.Username {
					t = 1
					if g.IsAllow == 0 {
						t = 0
						break
					}
				}
			}
			if t == 0 {
				break
			}
		}
	}
	return t
}

func getUserRelAccess(groups []sqlitemodel.GroupUsers, name string) (code bool, result []string) {
	result = make([]string, 0)
	if len(groups) > 0 {
		for _, item := range groups {
			if item.UserName == name {
				item.GroupName = strings.ReplaceAll(item.GroupName, "@", "")
				if item.GroupName == "admin" {
					code = true
				}
				result = append(result, item.GroupName)
			}
		}
	}
	return
}

// checkDefaultApp 如果有default=false表示前端的选择框不勾选
func checkDefaultApp(id string) bool {
	for _, item := range DefaultServe {
		if item.AppId == id {
			return true
		}
	}
	return false
}

func (ac *Access) createAccess(data []UserAccess, appid string, t bool, s string, _ string) (sql string) {
	if len(data) > 0 {
		for _, gs := range data {
			if !gs.IsCheck {
				// 没有选中时，默认权限
				continue
			}
			// 过滤 admin/guest/allusers
			//if gs.Username == admin || gs.Username == "admin" || gs.Username == "guest" || gs.Username == "allusers" {
			//if gs.Username == admin || gs.Username == "admin" || gs.Username == "allusers" {
			//	continue
			//}
			d := sqlitemodel.AppAccessUser{}
			d.AppId = appid
			if t {
				d.Username = gs.Username
			} else {
				d.Username = "@" + gs.Username
			}
			d.IsAllow = gs.IsAllow
			sql1 := fmt.Sprintf("('%s','%s',%d)", d.AppId, d.Username, d.IsAllow)
			if s == "" {
				s = sql1
			} else {
				s = s + "," + sql1
			}
		}
	}
	sql = s
	return
}

func (ac *Access) GetUserAccess(uname string) (appAccessUser []sqlitemodel.AppAccessUser, err error) {
	db := ac.db.DbGet()
	err = db.Model(sqlitemodel.AppAccessUser{}).Where("username=?", uname).Find(&appAccessUser).Error
	return
}

func setDefaultAccess(data []UserAccess, appid string) {
	s := ""
	pfile := filepath.Join(constant.PamConf, appid)
	if len(data) > 0 {
		for _, item := range data {
			name := item.Username
			if item.IsAllow == 1 {
				continue
			}
			if s == "" {
				s = name
			} else {
				s = s + "\n" + name
			}
		}
	}
	_, _ = utils.WriteFile(pfile, s)
}

//func GetAdmin() string {
//	return os.Getenv("admin")
//}

func (ac *Access) DelGroupAccess(name string) {
	db := ac.db.DbGet()
	_ = db.Model(sqlitemodel.AppAccessUser{}).Where("username=?", fmt.Sprintf("@%s", name)).Delete(sqlitemodel.AppAccessUser{}).Error
}

// t 0:all 1:file 2:folder
func GetAllFile(folder string, t int) (res []string, err error) {
	rd, err := os.ReadDir(folder)
	if err == nil {
		for _, fi := range rd {
			if t == 1 && fi.IsDir() {
				continue
			}
			if t == 2 && !fi.IsDir() {
				continue
			}
			res = append(res, fi.Name())
		}
	}
	return
}

func (ac *Access) DelUserAccess(uname string) {
	filelist, err := GetAllFile(constant.PamConf, 1)
	if !(err == nil && len(filelist) > 0) {
		return
	}
	for _, name := range filelist {
		fileconf := filepath.Join(constant.PamConf, name)
		slines := constant.ReadFileLine(fileconf)
		str := ""
		if len(slines) > 0 {
			for _, n := range slines {
				if n == uname {
					continue
				}
				if str == "" {
					str = n
				} else {
					str = fmt.Sprintf("%s\n%s", str, n)
				}
			}
		}
		_, _ = utils.WriteFile(fileconf, str)
	}
	db := ac.db.DbGet()
	err = db.Model(sqlitemodel.AppAccessUser{}).Where("username=?", uname).Delete(sqlitemodel.AppAccessUser{}).Error
}

func (ac *Access) GetDb() *gorm.DB {
	return ac.db.DbGet()
}

// UpdateUserDefaultAccess bug fix: X86 访问权限-应用 新建用户加入权限为禁止的用户组中，用户权限显示为禁止，但是SMB/AFP/FTP/AFTP还是能访问
// groupName = @allusers
func UpdateUserDefaultAccess(userName string, groupName string, db *gorm.DB) error {
	// 1. 查出表内所有内容
	var list []sqlitemodel.AppAccessUser

	db.Where("username=? and is_allow=0", groupName).Find(&list)
	if len(list) == 0 {
		return nil
	}
	// 2. 查出对应用户组对应用户权限有没有被限制
	for _, serve := range DefaultServe {
		// pam 只控制了AFP FTP SFTP SMB 四个权限
		if serve.AppId == "TOS" || serve.AppId == "filemanage" { // 这两个非 pam 控制的权限
			continue
		}
		updateUserAccess(userName, serve.AppId, groupName, list)
	}
	return nil
}

// updateUserAccess 更新pam文件
func updateUserAccess(username string, appId string, groupName string, list []sqlitemodel.AppAccessUser) {
	for _, item := range list {
		if appId != item.AppId {
			continue
		}
		if item.Username == groupName {
			updatePamConfig(appId, username)
		}
	}
}

func updatePamConfig(appId string, username string) {
	forbiddenUsers := constant.ReadFileLine(filepath.Join(constant.PamConf, appId))
	for _, user := range forbiddenUsers {
		// 如果原文件内有这个用户，说明以前被禁止过，但是用户删除的时候没有清理配置文件，直接返回
		if user == username {
			return
		}
	}
	forbiddenUsers = append(forbiddenUsers, username)
	constant.WriteUsersToFile(appId, forbiddenUsers)
}
